#include "InComTimer.h"
#include "InComPrint.h"

#define DEF_TAG "Timer"

static Com_s32 g_InComTimer_TZ_Sec = 0;
static time_t g_InComTimer_Mon_Yday[2][12] = {
    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};

Com_s32 InComTimer_IsLeap(Com_s32 year)
{
    return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
}

unsigned long int Ext_InComTimer_Date2TimeStamp(pStComCurrentTime dt, _OUT Com_s32 tz_sec)
{
    unsigned long int result = 0;
    Com_s32 i = 0;

    dt->mon -= 1;
    /* 以平年时间计算的秒数 */
    result = (dt->year - 1970) * 365 * 24 * 3600;
    result += (g_InComTimer_Mon_Yday[InComTimer_IsLeap(dt->year)][dt->mon] + dt->day - 1) * 24 * 3600;
    result += dt->hour * 3600 + dt->min * 60 + dt->sec - tz_sec;

    /* 加上闰年的秒数 */
    for (i = 1970; i < dt->year; i++)
    {
        if (InComTimer_IsLeap(i))
        {
            result += 24 * 3600;
        }
    }

    return (result);
}

long int InComTimer_Get_Us_TimeStamp(Com_void)
{
    struct timespec time;

    Com_s32 ret = clock_gettime(CLOCK_MONOTONIC, &time);
    if (ret < 0)
    {
        return 0;
    }

    return (long int)time.tv_sec * 1000 * 1000 + (long int)time.tv_nsec / 1000;
}

long int Ext_InComTimer_GetMs_TimeStamp(Com_void)
{
    struct timespec time;

    Com_s32 ret = clock_gettime(CLOCK_MONOTONIC, &time);
    if (ret < 0)
    {
        return 0;
    }

    return (long int)time.tv_sec * 1000 + ((long int)time.tv_nsec / (1000 * 1000));
}

Com_u32 Ext_InComTimer_TimeStamp2Date(Com_u32 timep, _OUT pStComCurrentTime date)
{
    Com_u32 days = 0;
    Com_u32 rem = 0;

    /* 计算天数 */
    days = (Com_u32)(timep / 86400);
    rem = (Com_u32)(timep % 86400);

    /* 计算年份 */
    Com_u16 year;
    for (year = 1970;; ++year)
    {
        Com_u16 leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
        Com_u16 ydays = leap ? 366 : 365;
        if (days < ydays)
        {
            break;
        }
        days -= ydays;
    }
    date->year = year;

    /* 计算月份 */
    static const Com_u16 days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    Com_u16 month;
    for (month = 0; month < 12; month++)
    {
        Com_u16 mdays = days_in_month[month];
        if (((month == 1) && ((year % 4 == 0) && (year % 100 != 0))) || (year % 400 == 0))
        {
            mdays = 29;
        }

        if (days < mdays)
        {
            break;
        }
        days -= mdays;
    }
    date->mon = month;
    date->mon += 1;

    /* 计算日期 */
    date->day = days + 1;

    /* 计算时间 */
    date->hour = rem / 3600;
    rem %= 3600;
    date->min = rem / 60;
    date->sec = rem % 60;

    return 0;
}

Com_u32 Ext_InComTimer_Sec(Com_void)
{
    struct timeval get_time;

    memset(&get_time, 0, sizeof(struct timeval));

    Com_s32 ret = gettimeofday(&get_time, NULL);
    if (ret != 0)
    {
        PRT_ERR(DEF_TAG, "tv_sec: %ld\n", get_time.tv_sec);
        return -1;
    }

    return get_time.tv_sec;
}

Com_u32 Ext_InComTimer_TZSec(Com_void)
{
    struct timeval get_time;

    memset(&get_time, 0, sizeof(struct timeval));
    Com_s32 ret = gettimeofday(&get_time, NULL);
    if (ret != 0)
    {
        PRT_ERR(DEF_TAG, "tv_sec: %ld\n", get_time.tv_sec);
        return -1;
    }

    return (get_time.tv_sec * 1000) + (get_time.tv_usec / 1000) + (g_InComTimer_TZ_Sec * 1000);
}

Com_s32 Ext_InComTimer_GetLocalTime(pStComCurrentTime cur_time)
{
    time_t t = time(NULL);
    struct tm *stime = localtime(&t);

    cur_time->year = 1900 + stime->tm_year;
    cur_time->mon = 1 + stime->tm_mon;
    cur_time->day = stime->tm_mday;
    cur_time->hour = stime->tm_hour;
    cur_time->min = stime->tm_min;
    cur_time->sec = stime->tm_sec;

    return 0;
}

Com_s32 Ext_InComTimer_GetTZLocalTime(pStComCurrentTime cur_time, Com_s32 tz)
{
    time_t t = time(NULL);
    t += tz;
    struct tm *stime = localtime(&t);

    cur_time->year = 1900 + stime->tm_year;
    cur_time->mon = 1 + stime->tm_mon;
    cur_time->day = stime->tm_mday;
    cur_time->hour = stime->tm_hour;
    cur_time->min = stime->tm_min;
    cur_time->sec = stime->tm_sec;

    return 0;
}

Com_s32 Ext_InComTimer_GetDate(pStComDateWeek date)
{
    time_t t = time(NULL);
    struct tm *stime = localtime(&t);

    date->year = 1900 + stime->tm_year;
    date->mon = 1 + stime->tm_mon;
    date->day = stime->tm_mday;
    date->hour = stime->tm_hour;
    date->min = stime->tm_min;
    date->sec = stime->tm_sec;
    date->week = stime->tm_wday;

    return 0;
}

Com_s32 Ext_InComTimer_GetTZDate(pStComDateWeek date, Com_s32 tz)
{
    time_t t = time(NULL);
    t += tz;
    struct tm *stime = localtime(&t);

    date->year = 1900 + stime->tm_year;
    date->mon = 1 + stime->tm_mon;
    date->day = stime->tm_mday;
    date->hour = stime->tm_hour;
    date->min = stime->tm_min;
    date->sec = stime->tm_sec;
    date->week = stime->tm_wday;

    return 0;
}

Com_u32 Ext_InComTimer_GetSec(Com_u8 set_time, pStComSecondTimer timer)
{
    timer->now_time = (Com_u32)(InComTimer_Get_Us_TimeStamp() / 1000 / 1000);

    if (set_time == DEF_COM_SET_NEW_TIME)
    {
        timer->last_time = (Com_u32)(InComTimer_Get_Us_TimeStamp() / 1000 / 1000);
    }
    return (Com_u32)(timer->now_time - timer->last_time);
}

Com_u64 Ext_InComTimer_GetMs(Com_u8 set_time, pStComMicroSecondsTimer timer)
{
    timer->now_time = (Com_u64)(InComTimer_Get_Us_TimeStamp() / 1000);

    if (set_time == DEF_COM_SET_NEW_TIME)
    {
        timer->last_time = (Com_u64)(InComTimer_Get_Us_TimeStamp() / 1000);
    }
    return (Com_u64)(timer->now_time - timer->last_time);
}

Com_void Ext_InComTimer_SetTZSec(Com_s32 val)
{
    if ((val < ((-12) * 60 * 60)) || (val > (12 * 60 * 60)))
    {
        val = 0;
        PRT_WARN(DEF_TAG, "timezone is error\n");
    }

    g_InComTimer_TZ_Sec = val;
}

Com_s32 Ext_InComTimer_GetTZSec(Com_void)
{
    return g_InComTimer_TZ_Sec;
}
